package facebook

import (
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/go-resty/resty/v2"
	"github.com/google/uuid"
	"net/http"
	"net/url"
	"sieve_engine/execute"
	"sieve_engine/lib/config"
	"sieve_engine/lib/log"
	"sieve_engine/task"
	"strings"
	"time"
)

const (
	facebookApi = "https://graph.facebook.com/recover_accounts?q=%s&friend_name&qs&summary=true&device_id=%s&src=fb4a_account_recovery&machine_id&locale=en_US&client_country_code=US&method=GET&fb_api_req_friendly_name=accountRecoverySearch&fb_api_caller_class=com.facebook.account.recovery.fragment.AccountSearchFragment&access_token=350685531728|62f8ce9f74b12f84c123cc23437a4a32"
)

type faceBookExecutor struct {
	*execute.DefaultExecutor
	execute.DefaultNetWorkConfig
}

var (
	fbExecutor execute.TaskExecutorInterface = &faceBookExecutor{
		DefaultExecutor: &execute.DefaultExecutor{
			Debug:                *config.Bool("sieve.facebook.debug", true, "debug"),
			PrintMore:            *config.Bool("sieve.facebook.print_more", true, "print more"),
			ProxyEnable:          *config.Bool("sieve.facebook.proxy.enable", true, "proxy enable"),
			ProxyDebug:           *config.Bool("sieve.facebook.proxy.debug", true, "proxy debug"),
			NetworkErrRetryCount: *config.Int("sieve.facebook.proxy.retry_count_if_network_error", -1, "retry count if network error"),
			DefaultResultCache:   execute.NewDefaultResultCache(),
		},
	}
)

func init() {

	execute.Register("fb", fbExecutor)
}

func (s *faceBookExecutor) GetNetWorkConfig() *execute.NetWorkConfig {

	def := execute.GetSycTransportSetting()
	def.DialTimeOut = 3 * time.Second
	def.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	def.IdleConnTimeout = time.Second * 90
	def.ExpectContinueTimeout = time.Second * 10
	def.DisableKeepAlives = false

	return &execute.NetWorkConfig{
		TransPortConfig: def,
		HttpConfig: execute.HttpConfig{
			Timeout: time.Second * 10,
		},
	}
}

func (s *faceBookExecutor) Execute(params execute.TaskExecutorParams, taskProgress *task.Result) error {

	return s.DefaultExecutor.Execute(params, taskProgress, s.getApi)
}

func (s *faceBookExecutor) getApi(phone string, params execute.TaskExecutorParams, roundTripper http.RoundTripper, reuseProxy bool) (http.RoundTripper, bool, *url.URL, error) {

	if s.Debug {
		return nil, true, nil, nil
	}

	var (
		client   = resty.New()
		proxyURL *url.URL
		timeout  = s.GetNetWorkConfig().HttpConfig.Timeout
	)

	if !reuseProxy {
		/* 如果不带用代理复用策略，那么就重新获取代理 */
		roundTripper, proxyURL = execute.SetDefaultTransport("", s.GetNetWorkConfig())
	}

	start := time.Now()
	// client.SetCloseConnection(true)
	client.SetTransport(roundTripper).SetTimeout(timeout).EnableTrace()
	resp, err := client.SetDebug(s.ProxyDebug).R().SetHeaders(map[string]string{
		// "Connection":      "close",
		"Accept-Language": "en-US",
		"User-Agent":      "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36",
	}).
		Get(fmt.Sprintf("%v", fmt.Sprintf(facebookApi, phone, uuid.NewString())))
	if err != nil {
		if s.PrintMore {
			log.Err(fmt.Sprintf("任务ID:%v 任务批次:%v 请求接口错误:(%v %v %+v)", params.TaskInfo.Id, params.BatchIndex, err, resp.Error(), resp))
		}
		return roundTripper, false, proxyURL, err
	}
	if s.PrintMore {
		log.Info(fmt.Sprintf("任务ID:%v 任务批次:%v 请求接口耗时：%v", params.TaskInfo.Id, params.BatchIndex, time.Since(start).String()))
	}

	var (
		body = resp.Body()
	)

	if resp.StatusCode() != http.StatusOK {
		if strings.Contains(string(body), "error") {
			resultBean := new(FBErrorResult)
			if err = json.Unmarshal(body, resultBean); err != nil {
				return nil, false, proxyURL, err
			} else {
				if s.Debug {
					log.Info(fmt.Sprintf("任务ID:%v 任务批次:%v 请求接口错误(%v)", params.TaskInfo.Id, params.BatchIndex, resultBean.Error.ErrorUserMsg))
				}
				if strings.Contains(resultBean.Error.ErrorUserMsg, "It looks like you were misusing this feature by going too fast") {
					// return nil, false, proxyURL, errors.New(resultBean.Error.ErrorUserMsg)
					return nil, false, proxyURL, execute.RequestErrorForbidden
				}

				if strings.Contains(resultBean.Error.Message, "An unknown error has occurred") ||
					strings.Contains(resultBean.Error.Message, "An unknown error occurred") ||
					strings.Contains(resultBean.Error.Message, "Service temporarily unavailable") ||
					strings.Contains(resultBean.Error.Message, "The action attempted has been deemed abusive or is otherwise disallowed") {
					return roundTripper, false, proxyURL, execute.RequestErrorForbidden
				}
			}

			return nil, false, proxyURL, execute.RequestErrorRefusedError
		}

		return nil, false, proxyURL, errors.New(fmt.Sprintf("http failed,code:%d", resp.StatusCode()))
	}

	resultBean := new(FBResultNormalBean)
	err = json.Unmarshal(body, resultBean)

	if err != nil {
		return nil, false, proxyURL, err
	}
	if resultBean.Summary.TotalCount > 0 {
		return roundTripper, true, proxyURL, nil
	}
	// TaskLogger.Error("facebook parse content CodeGiveUp:", zap.String("content", content))
	return roundTripper, false, proxyURL, nil
}
